<!doctype HTML public "-//W3C//DTD HTML 4.0 Frameset//EN">
<html>
<title>Bloomberg Development Environment</title>
<html>
<pre>
// Copyright 2014-2023 Bloomberg Finance L.P.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// bmqa_message.h                                                     -*-C++-*-
#ifndef INCLUDED_BMQA_MESSAGE
#define INCLUDED_BMQA_MESSAGE

//@PURPOSE: Provide the application with a message data object.
//
//@CLASSES:
//  bmqa::Message:                   message received from/sent to a queue
//  bmqa::MessageConfirmationCookie: cookie for async message confirmation
//
//@SEE_ALSO:
//  bmqa::MessageEvent       : Mechanism for data event notification.
//  bmqa::MessageEventBuilder: Builder for &#39;bmqa::MessageEvent&#39;
//
//@DESCRIPTION: A &#39;bmqa::Message&#39; represents the data message to put on a
// queue, or retrieved from a queue.  It is composed of the following fields:
//
//: o A message GUID, which is a printable string generated by the broker to
//:    uniquely identify this message.
//:
//: o A correlation Id, which is a user-provided identifier for the message.
//:
//: o A queue Id, to map with the queue this message is associated with.
//:
//: o The payload, which is opaque to the framework. At some point, framework
//:   may provide utilities to encode and decode schema messages using various
//:   CODECs.  For example, a JavaScript publisher may publish a message into
//:   a queue using a JSON object as payload, and the consumer may be receiving
//:   that payload as a BER-encoded schema object.
//:
//: o At some point, system properties such as version, encoding, timestamp,
//:   priority, message group and &quot;reply-to&quot; will be supported. System
//:   properties will be used by the broker; for example to deliver
//:   high-priority messages first or to filter based on a minimum version.
//:
//: o At some point, application-defined message properties will also be
//:   supported, where properties are a list of name-value pairs.
//
// A &#39;bmqa::MessageConfirmationCookie&#39; is a small object which allows to
// confirm a &#39;bmqa::Message&#39; asynchronously without having to hold on to the
// entire message.  This can be useful when, for example, the message is
// decoded in the event handler, and the resulting object is enqueued for
// asynchronous processing, along with that small cookie object for confirming
// the message once successfully processed.

// BMQ
#include &lt;bmqscm_version.h&gt;
#include &lt;bmqa_queueid.h&gt;
#include &lt;bmqt_compressionalgorithmtype.h&gt;
#include &lt;bmqt_correlationid.h&gt;
#include &lt;bmqt_messageguid.h&gt;

// BDE
#include &lt;bdlbb_blob.h&gt;
#include &lt;bsl_cstddef.h&gt; // for &#39;size_t&#39;
#include &lt;bsl_iosfwd.h&gt;
#include &lt;bsl_memory.h&gt;
#include &lt;bsl_string.h&gt;
#include &lt;bslma_allocator.h&gt;
#include &lt;bsls_annotation.h&gt;


namespace BloombergLP {

// FORWARD DECLARATION
namespace bmqimp { class Event; }

namespace bmqa {

// FORWARD DECLARATION
class MessageProperties;


                             // ==================
                             // struct MessageImpl
                             // ==================

struct MessageImpl {
    // Struct containing the internal (private) members of Message (That is so
    // that we can access private members of Message to initialize it, without
    // having to expose them publicly).
    //
    // IMPLEMENTATION NOTE: If adding new data members to this struct that are
    //                      lazily populated (such as &#39;queueId&#39; or
    //                      &#39;correlationId&#39;), then they should be reset in
    //                      &#39;bmqa::MessageIterator.nextMessage()&#39;.

    // PUBLIC DATA
    bmqimp::Event                  *d_event_p;
                                      // Pointer to the Event this message is
                                      // associated with

    bsl::shared_ptr&lt;bmqimp::Event&gt;  d_clonedEvent_sp;
                                      // May point to a bmqimp::Event (in case
                                      // this Message is a clone)

    bmqa::QueueId                   d_queueId;
                                      // QueueId this message is associated
                                      // with

    bmqt::CorrelationId             d_correlationId;
                                      // CorrelationId this message is
                                      // associated with

#ifdef BMQ_ENABLE_MSG_GROUPID
    bsl::string                     d_groupId;
                                      // Optional Group Id this message is
                                      // associated with
#endif
};


                      // ===============================
                      // class MessageConfirmationCookie
                      // ===============================

class MessageConfirmationCookie {
    // Cookie for async message confirmation.

  private:
    // DATA
    bmqa::QueueId     d_queueId;
                       // QueueID associated to this cookie

    bmqt::MessageGUID d_guid;
                       // GUID associated to this cookie

  public:
    // CREATORS
    MessageConfirmationCookie();
        // Create an unset instance of this class.

    MessageConfirmationCookie(const QueueId&amp;           queueId,
                              const bmqt::MessageGUID&amp; messageGUID);
        // Create an instance with the specified &#39;queueId&#39; and &#39;messageGUID&#39;.
        // Users should not use that constructor directly, but rather load the
        // message cookie from an existing &#39;bmqa::Message&#39; with the
        // &#39;bmqa::Message::confirmationCookie&#39; accessor.

    // ACCESSORS
    const QueueId&amp; queueId() const;
        // Return the queue ID of the message with which this confirmation
        // cookie is associated.

    const bmqt::MessageGUID&amp; messageGUID() const;
        // Return message GUID of the message with which this confirmation
        // cookie is associated.
};

                               // =============
                               // class Message
                               // =============

class Message {
    // A message sent/received to/from the BlazingMQ broker.

#ifdef BMQ_ENABLE_MSG_GROUPID
  public:
    // CONSTANTS
    static const int k_GROUP_ID_MAX_LENGTH = 31;
        // Constant representing the maximum length of a Group Id string.
#endif

  private:
    // DATA
    mutable MessageImpl d_impl; // pimpl

  private:
    // PRIVATE ACCESSORS
    bool isInitialized() const;
        // Return true if the message has been initialized with an underlying
        // event (and thus is valid), and false otherwise.  Any operation
        // except assignment or destruction on an uninitialized message is an
        // error.

  public:
    // CREATORS
    explicit Message();
        // Create an invalid message having no content. Only valid operations
        // on an invalid message instance are assignment and destruction.

    //! Message(const Message&amp; other) = default;
        // Create a message from the specified &#39;other&#39; instance.

    //! ~Message() = default;
        // Destructor

    // MANIPULATORS
    //! Message&amp; operator=(const Message&amp; other) = default;
        // Assign to this object the value of the specified &#39;other&#39; instance
        // and return a reference to this object.

    Message&amp; setData(const bdlbb::Blob *data) BSLS_ANNOTATION_DEPRECATED;
        // Set the payload of this message to the blob pointed to by the
        // specified &#39;data&#39;.  Behavior is undefined unless &#39;data&#39; is non-null
        // and payload&#39;s length is greater than zero.  Note that payload
        // pointed to by &#39;data&#39; is *not* copied right away, and should not be
        // destroyed or modified until this message has been packed (see
        // &#39;bmqa::MessageEventBuilder&#39; component level documentation for
        // correct usage).
        //
        // This method is deprecated, please use &#39;setDataRef()&#39; instead.

    Message&amp; setData(const char *data,size_t length)
                                                    BSLS_ANNOTATION_DEPRECATED;
        // Set the payload of this message to the specified &#39;length&#39; bytes
        // starting at the specified &#39;data&#39; address.  The behavior is undefined
        // unless &#39;data&#39; is non-null and &#39;length&#39; is greater than zero.  Note
        // that payload pointed to by &#39;data&#39; is *not* copied right away, and
        // should not be destroyed or modified until this message has been
        // packed (see &#39;bmqa::MessageEventBuilder&#39; component level
        // documentation for correct usage).
        //
        // This method is deprecated, please use &#39;setDataRef()&#39; instead.

    Message&amp; setDataRef(const bdlbb::Blob *data);
        // Set the payload of this message to the blob pointed to by the
        // specified &#39;data&#39;.  Behavior is undefined unless &#39;data&#39; is non-null
        // and payload&#39;s length is greater than zero.  Note that payload
        // pointed to by &#39;data&#39; is *not* copied right away, and should not be
        // destroyed or modified until this message has been packed (see
        // &#39;bmqa::MessageEventBuilder&#39; component level documentation for
        // correct usage).

    Message&amp; setDataRef(const char *data,
                        size_t      length);
        // Set the payload of this message to the specified &#39;length&#39; bytes
        // starting at the specified &#39;data&#39; address.  The behavior is undefined
        // unless &#39;data&#39; is non-null and &#39;length&#39; is greater than zero.  Note
        // that payload pointed to by &#39;data&#39; is *not* copied right away, and
        // should not be destroyed or modified until this message has been
        // packed (see &#39;bmqa::MessageEventBuilder&#39; component level
        // documentation for correct usage).

    Message&amp; setPropertiesRef(const MessageProperties *properties);
        // Set the properties of this message to the &#39;MessageProperties&#39;
        // instance pointed by the specified &#39;properties&#39;.  Behavior is
        // undefined unless &#39;properties&#39; is non-null.  Note that properties are
        // *not* copied right away, and should not be destroyed or modified
        // until this message has been packed (see &#39;bmqa::MessageEventBuilder&#39;
        // component level documentation for correct usage).

    Message&amp; clearPropertiesRef();
        // Clear out and properties associated with this message.  Note that if
        // there are no properties associated with this message, this method
        // has no effect.  Also note that the associated &#39;MessageProperties&#39;
        // instance, if any, is not modified; it&#39;s simply dissociated from this
        // message.

    Message&amp; setCorrelationId(const bmqt::CorrelationId&amp; correlationId);
        // Set correlation ID of this message to the specified &#39;correlationId&#39;.

    Message&amp;
    setCompressionAlgorithmType(bmqt::CompressionAlgorithmType::Enum value);
        // Set the Compression algorithm type of the current message to the
        // specified &#39;value&#39; and return a reference offering modifiable access
        // to this object.

#ifdef BMQ_ENABLE_MSG_GROUPID
    Message&amp; setGroupId(const bsl::string&amp; groupId);
        // Set Group Id of this message to the specified &#39;groupId&#39;.  The
        // &#39;groupId&#39; must be a null-terminated string with up to
        // &#39;Message::k_GROUP_ID_MAX_LENGTH&#39; characters (excluding the
        // terminating &#39;\0&#39;).  The behavior is undefined if the message is not
        // a started, &#39;PUT&#39; message or if &#39;groupId&#39; is empty.

    Message&amp; clearGroupId();
        // Clear the Group Id of this message.  The behavior is undefined if
        // the message is not a started, &#39;PUT&#39; message.
#endif

    // ACCESSORS
    bool isValid() const; // TBD:BSLS_ANNOTATION_DEPRECATED
        // Return true if the message is valid, and false otherwise.  Any
        // operation except assignment or destruction on an invalid message is
        // an error.
        //
        // This method is deprecated.

    operator bool() const; // TBD:BSLS_ANNOTATION_DEPRECATED
        // Same as &#39;isValid&#39;.
        //
        // This method is deprecated.

    Message clone(bslma::Allocator *basicAllocator = 0) const;
        // Return a copy of this message, using the optionally specified
        // &#39;basicAllocator&#39; with the copy holding all the data of this instance
        // and not backed by any &#39;MessageEvent&#39;.  Note that this operation
        // does *not* copy underlying data.

    const bmqa::QueueId&amp; queueId() const;
        // Return the queue ID indicating the queue for which this message has
        // been received from.  The behavior is undefined unless this instance
        // represents a &#39;PUT&#39;, &#39;PUSH&#39; or &#39;ACK&#39; message.

    const bmqt::CorrelationId&amp; correlationId() const;
        // Return the correlation Id associated with this message.  The
        // behavior is undefined unless this instance represents a &#39;PUT&#39; or an
        // &#39;ACK&#39;, or a &#39;PUSH&#39; message.  Note that in case of failure to accept
        // a &#39;PUT&#39; message, BlazingMQ sends an &#39;ACK&#39; message with failed
        // status, even if an &#39;ACK&#39; message was not requested by the
        // application (i.e., no correlationId was specified when posting the
        // message).  In such cases, correlationId associated with the &#39;ACK&#39;
        // message will be unset, and as such, application *must* check for
        // that by invoking &#39;isUnset&#39; on the returned correlationId object.  In
        // the case of a &#39;PUSH&#39; message, the return value is the one specified
        // as the correlation id of the corresponding Subscription.  Invoking
        // &#39;thePointer&#39;, &#39;theNumeric&#39; or &#39;theSharedPtr&#39; on an unset
        // correlationId instance will lead to undefined behavior.

    bmqt::CompressionAlgorithmType::Enum compressionAlgorithmType() const;
        // Return Compression algorithm type of the current message. Behavior
        // is undefined unless this instance represents a &#39;PUT&#39; or &#39;PUSH&#39;
        // message.

#ifdef BMQ_ENABLE_MSG_GROUPID
    const bsl::string&amp; groupId() const;
        // Return the Group Id associated with this message.  The behavior is
        // undefined unless this instance represents a &#39;PUT&#39; or a &#39;PUSH&#39;
        // message and &#39;hasGroupId()&#39; returns &#39;true&quot;.
#endif

    const bmqt::MessageGUID&amp; messageGUID() const;
        // Return the unique message Id generated by the SDK for this message.
        // The behavior is undefined unless this instance represents a &#39;PUT&#39;,
        // a &#39;PUSH&#39; or an &#39;ACK&#39; message.

    MessageConfirmationCookie confirmationCookie() const;
        // Return a cookie which can be used to confirm this message.  The
        // behavior is undefined unless this instance represents a &#39;PUSH&#39;
        // message.

    int ackStatus() const;
        // Return the status of the &#39;ACK&#39; message.  The behavior is undefined
        // unless this instance represents an &#39;ACK&#39; message.  This value
        // correspond to the &#39;bmqt::AckResult::Enum&#39; enum.

    int getData(bdlbb::Blob *blob) const;
        // Load into the specified &#39;blob&#39; the payload of the message, if any.
        // Return zero if the message has a payload and non-zero value
        // otherwise.  The behaviour is undefined unless this instance
        // represents a &#39;PUT&#39; or &#39;PUSH&#39; message.  Note that for efficiency,
        // application should fetch payload once and cache the value, instead
        // of invoking this method multiple times on a message.

    int dataSize() const;
        // Return the number of bytes in the payload.  The behaviour is
        // undefined unless this instance represents a &#39;PUT&#39; or a &#39;PUSH&#39;
        // message.  Note that for efficiency, application should fetch payload
        // size once and cache the value, instead of invoking this method
        // multiple times on a message.

    bool hasProperties() const;
        // Return &#39;true&#39; if this instance has at least one message property
        // associated with it, &#39;false&#39; otherwise.  Behavior is undefined unless
        // this instance represents a &#39;PUT&#39; or a &#39;PUSH&#39; message.

#ifdef BMQ_ENABLE_MSG_GROUPID
    bool hasGroupId() const;
        // Return whether this message has an associated GroupId set.  The
        // behavior is undefined unless this instance represents a &#39;PUT&#39; or a
        // &#39;PUSH&#39; message.
#endif

    int loadProperties(MessageProperties *buffer) const;
        // Load into the specified &#39;buffer&#39; the properties associated with this
        // message.  Return zero on success, and a non-zero value otherwise.
        // Behavior is undefined unless this instance represents a &#39;PUT&#39; or a
        // &#39;PUSH&#39; message, and unless &#39;buffer&#39; is non-null.  Note that if there
        // are no properties associated with this message, zero will be
        // returned and the &#39;MessageProperties&#39; instance pointed by &#39;buffer&#39;
        // will be cleared.  Also note that for efficiency, application should
        // fetch properties once and cache the value, instead of invoking this
        // method multiple times on a message.

    bsl::ostream&amp; print(bsl::ostream&amp; stream,
                        int           level          = 0,
                        int           spacesPerLevel = 4) const;
        // Format this object to the specified output &#39;stream&#39; at the (absolute
        // value of) the optionally specified indentation &#39;level&#39; and return a
        // reference to &#39;stream&#39;.  If &#39;level&#39; is specified, optionally specify
        // &#39;spacesPerLevel&#39;, the number of spaces per indentation level for
        // this and all of its nested objects.  If &#39;level&#39; is negative,
        // suppress indentation of the first line.  If &#39;spacesPerLevel&#39; is
        // negative format the entire output on one line, suppressing all but
        // the initial indentation (as governed by &#39;level&#39;).  If &#39;stream&#39; is
        // not valid on entry, this operation has no effect.
};

// FREE OPERATORS
bsl::ostream&amp; operator&lt;&lt;(bsl::ostream&amp; stream, const Message&amp; rhs);
    // Format the specified &#39;rhs&#39; to the specified output &#39;stream&#39; and return a
    // reference to the modifiable &#39;stream&#39;.


// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================


                               // -------------
                               // class Message
                               // -------------

inline
Message&amp;
Message::setData(const bdlbb::Blob *data)
{
    return setDataRef(data);
}

inline
Message&amp;
Message::setData(const char *data,
                 size_t      length)
{
    return setDataRef(data, length);
}

inline
Message::operator bool() const
{
    return isInitialized();
}

                        // -------------------------------
                        // class MessageConfirmationCookie
                        // -------------------------------

inline
MessageConfirmationCookie::MessageConfirmationCookie()
: d_queueId()
, d_guid()
{
    // NOTHING
}

inline
MessageConfirmationCookie::MessageConfirmationCookie(
                                          const bmqa::QueueId&amp;     queueId,
                                          const bmqt::MessageGUID&amp; messageGUID)
: d_queueId(queueId)
, d_guid(messageGUID)
{
    // NOTHING
}

// ACCESSORS
inline
const QueueId&amp;
MessageConfirmationCookie::queueId() const
{
    return d_queueId;
}

inline
const bmqt::MessageGUID&amp;
MessageConfirmationCookie::messageGUID() const
{
    return d_guid;
}
}  // close package namespace


                               // -------------
                               // class Message
                               // -------------

// FREE OPERATORS
inline
bsl::ostream&amp;
bmqa::operator&lt;&lt;(bsl::ostream&amp;        stream,
                 const bmqa::Message&amp; rhs)
{
    return rhs.print(stream, 0, -1);
}

}  // close enterprise namespace

#endif
</pre>
</body>
</html>
